package cn.b.sky.shop.activiti.utils;

import cn.b.sky.shop.activiti.service.DeploymentNodeService;
import cn.b.sky.common.constants.SystemConstant;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.persistence.entity.ModelEntity;
import org.activiti.engine.impl.util.IoUtil;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.InputStreamReader;
import java.util.List;

/**
 *
 * <strong>Title : ActivitiModelInit.java<br></strong>
 * <strong>Package : cn.b.sky.shop.activiti.listener<br></strong>
 * <strong>Description : </strong>流程模型初始化<br>
 * <strong>Create on : 2016-6-14 上午10:21:54<br></strong>
 * <p>
 * @author lidapeng<br>
 * @version <strong>v1.0.0</strong><br>
 * <br>
 * <strong>修改历史:</strong><br>
 * 修改人|修改日期|修改描述<br>
 * -------------------------------------------<br>
 * <br>
 * <br>
 */
@Component
public class ActivitiModelInit implements InitializingBean {

  private static final Logger LOGGER = LoggerFactory.getLogger(ActivitiModelInit.class);

  @Autowired
  private RepositoryService repositoryService;
  @Autowired
  private DeploymentNodeService deploymentNodeService;

  private Resource[] deploymentResources = new Resource[0];

  @Value("${tenant.default.code:1001}")
  private String defaultTenantCode;
  /**
   * 是否更新资源
   */
  @Value("${activitMode.updateResource:true}")
  private  boolean updateResource = true;

  /**
   * 是否直接发布
   */
  @Value("${activitMode.deployModel:true}")
  private boolean deployModel = true;

  @Override
  public void afterPropertiesSet() throws Exception {
    if(updateResource){
      init(null);
    }
  }

  /**
   *
   * <strong>Title : init<br></strong>
   * <strong>Description : </strong>程序启动初始化流程数据<br>
   * <strong>Create on : 2016-6-24 下午4:56:37<br></strong>
   * <p>
   * @param tenantId
   * void
   * @throws
   * @author lidapeng<br>
   * @version <strong>v1.0.0</strong><br>
   * <br>
   * <strong>修改历史:</strong><br>
   * 修改人 | 修改日期 | 修改描述<br>
   * -------------------------------------------<br>
   * <br>
   * <br>
   */
  public void init(String tenantId) {
    if (!StringUtils.isBlank(tenantId)) {
      defaultTenantCode = tenantId;
    }
    if ((this.deploymentResources == null) || (this.deploymentResources.length == 0)) {
      return;
    }
    for (Resource resource : this.deploymentResources) {
      try {
        InputStreamReader in = new InputStreamReader(resource.getInputStream(), "UTF-8");
        BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
        XMLInputFactory xif = XMLInputFactory.newInstance();
        XMLStreamReader xtr = xif.createXMLStreamReader(in);
        BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);
        xmlConverter.convertToBpmnModel(xtr);
        if (bpmnModel.getMainProcess() == null || bpmnModel.getMainProcess().getId() == null) {
          LOGGER.error("校验失败，请确认文件中包含有效的BPMN定义！");
        } else {
          if (bpmnModel.getLocationMap().size() == 0) {
            LOGGER.error("校验失败，请确认文件中包含BPMN DI信息!");
          } else {
            String processName = bpmnModel.getMainProcess().getName();
            //流程需要和模型类型关联，这里的resource必须是从系统中导出的
            if(bpmnModel.getMainProcess().getId().indexOf(SystemConstant.MIDDLE_SPLIT) == -1){
              LOGGER.error("模板校验失败，请确认流程id包含流程模型类型!");
              continue;
            }
            String[] processKey = bpmnModel.getMainProcess().getId().split(SystemConstant.MIDDLE_SPLIT);
            bpmnModel.getMainProcess().setId(processKey[1]);
            //设置category
            Model model = repositoryService.createModelQuery().modelKey(processKey[1])
                .modelTenantId(defaultTenantCode).singleResult();

            if(model != null && !StringUtils.isBlank(model.getKey())){
              LOGGER.info("模型已存在!"+model.getKey());
              continue;
            }
            if (StringUtils.isBlank(processName)) {
              processName = processKey[1];
            }
            ModelEntity modelData = (ModelEntity) repositoryService.newModel();
            ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
            modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processName);
            modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
            modelData.setMetaInfo(modelObjectNode.toString());
            modelData.setName(processName);
            //设置category
            if(model!=null){
              modelData.setCategory(model.getCategory());
            }else{
              modelData.setCategory(processKey[0]);
            }
            modelData.setKey(processKey[1]);
            modelData.setTenantId(defaultTenantCode);
            /**
             * 部署的流程模型直接发布，这里发布的可能不生效
             */
            if (deployModel) {
              Deployment deployment = this.repositoryService.createDeployment().name(modelData.getName())
                  .tenantId(modelData.getTenantId())
                  .addString(processName, new String(xmlConverter.convertToXML(bpmnModel), "UTF-8")).deploy();
              modelData.setDeploymentId(deployment.getId());
            }
            repositoryService.saveModel(modelData);

            BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
            ObjectNode editorNode = jsonConverter.convertToJson(bpmnModel);
            repositoryService.addModelEditorSource(modelData.getId(), editorNode.toString()
                .getBytes("utf-8"));
            ProcessEngineConfigurationImpl pconf = (ProcessEngineConfigurationImpl) ProcessEngines.getDefaultProcessEngine()
                .getProcessEngineConfiguration();
            byte[] diagramBytes = IoUtil.readInputStream(
                pconf.getProcessDiagramGenerator().generateDiagram(bpmnModel, "png",
                    pconf.getProcessEngineConfiguration().getActivityFontName(), pconf.getLabelFontName(),
                    pconf.getProcessEngineConfiguration().getClassLoader()), null);
            repositoryService.addModelEditorSourceExtra(modelData.getId(), diagramBytes);
          }
        }
      } catch (Exception e) {
        throw new ActivitiException("couldn't auto deploy resource '" + resource + "': "
            + e.getMessage(), e);
      }
    }
  }

  /**
   *
   * <strong>Title : copyActivitiModel<br></strong>
   * <strong>Description : </strong>复制工厂的时候进行流程模型copy<br>
   * <strong>Create on : 2016-6-24 下午4:55:54<br></strong>
   * <p>
   * @param sourceSiteCode
   * @param targetSiteCode
   * void
   * @throws
   * @author lidapeng<br>
   * @version <strong>v1.0.0</strong><br>
   * <br>
   * <strong>修改历史:</strong><br>
   * 修改人 | 修改日期 | 修改描述<br>
   * -------------------------------------------<br>
   * <br>
   * <br>
   */
  public void copyActivitiModel(String sourceSiteCode, String targetSiteCode){
    if(StringUtils.isBlank(sourceSiteCode)){
      return;
    }
    List<Model> models = repositoryService.createModelQuery().modelTenantId(sourceSiteCode).list();
    //复制model
    if(models != null && !models.isEmpty()){
      for(Model model : models){
        ModelEntity entity = (ModelEntity) model;
        String oldId = entity.getId();
        //直接部署
        if (deployModel) {
          try {
            ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(this.repositoryService
                .getModelEditorSource(entity.getId()));
            byte[] bpmnBytes = null;

            BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(modelNode);

            bpmnBytes = new BpmnXMLConverter().convertToXML(bpmnModel);

            String processName = entity.getName() + ".bpmn20.xml";

            Deployment deployment = this.repositoryService.createDeployment().name(entity.getName())
                .tenantId(targetSiteCode).addString(processName, new String(bpmnBytes, "UTF-8")).deploy();
            //已发布
            entity.setDeploymentId(deployment.getId());
            //发布流程时复制节点配置信息
            deploymentNodeService.copyModelNodeToDeploymentNode(oldId, sourceSiteCode, deployment.getId());
          } catch (Exception e) {
            LOGGER.error("根据模型部署流程失败：modelId={}", entity.getId(), e);
          }
        }
        entity.setId(null);
        entity.setTenantId(targetSiteCode);
        repositoryService.saveModel(entity);
        repositoryService.addModelEditorSource(entity.getId(),
            repositoryService.getModelEditorSource(entity.getId()));
        repositoryService.addModelEditorSourceExtra(entity.getId(),
            repositoryService.getModelEditorSourceExtra(entity.getId()));
      }
    }
  }

  public RepositoryService getRepositoryService() {
    return repositoryService;
  }

  public void setRepositoryService(RepositoryService repositoryService) {
    this.repositoryService = repositoryService;
  }

  public void setDeploymentNodeService(DeploymentNodeService deploymentNodeService) {
    this.deploymentNodeService = deploymentNodeService;
  }

  public Resource[] getDeploymentResources() {
    return deploymentResources;
  }

  public void setDeploymentResources(Resource[] deploymentResources) {
    this.deploymentResources = deploymentResources;
  }

  public String getDefaultTenantCode() {
    return defaultTenantCode;
  }

  public void setDefaultTenantCode(String defaultTenantCode) {
    this.defaultTenantCode = defaultTenantCode;
  }

  public void setUpdateResource(boolean updateResource) {
    this.updateResource = updateResource;
  }

  public void setDeployModel(boolean deployModel) {
    this.deployModel = deployModel;
  }

}
